use super::{RespValue, ProtocolError, ProtocolResult, RedisCommand};
use bytes::{Buf, Bytes, BytesMut};

/// RESP protocol parser
pub struct RespParser;

impl RespParser {
    /// Parse a complete RESP value from bytes
    pub fn parse(buf: &mut BytesMut) -> ProtocolResult<Option<RespValue>> {
        if buf.is_empty() {
            return Ok(None);
        }

        let mut cursor = std::io::Cursor::new(&buf[..]);

        match Self::parse_value(&mut cursor) {
            Ok(value) => {
                let pos = cursor.position() as usize;
                buf.advance(pos);
                Ok(Some(value))
            }
            Err(ProtocolError::IncompleteData) => Ok(None),
            Err(e) => Err(e),
        }
    }

    /// Parse a RESP value from cursor
    fn parse_value(cursor: &mut std::io::Cursor<&[u8]>) -> ProtocolResult<RespValue> {
        if !cursor.has_remaining() {
            return Err(ProtocolError::IncompleteData);
        }

        let type_byte = cursor.get_u8();

        match type_byte {
            b'+' => Self::parse_simple_string(cursor),
            b'-' => Self::parse_error(cursor),
            b':' => Self::parse_integer(cursor),
            b'$' => Self::parse_bulk_string(cursor),
            b'*' => Self::parse_array(cursor),
            _ => Err(ProtocolError::InvalidFormat(format!(
                "Unknown type byte: {}",
                type_byte as char
            ))),
        }
    }

    /// Parse simple string: +OK\r\n
    fn parse_simple_string(cursor: &mut std::io::Cursor<&[u8]>) -> ProtocolResult<RespValue> {
        let line = Self::read_line(cursor)?;
        Ok(RespValue::SimpleString(line))
    }

    /// Parse error: -Error message\r\n
    fn parse_error(cursor: &mut std::io::Cursor<&[u8]>) -> ProtocolResult<RespValue> {
        let line = Self::read_line(cursor)?;
        Ok(RespValue::Error(line))
    }

    /// Parse integer: :1000\r\n
    fn parse_integer(cursor: &mut std::io::Cursor<&[u8]>) -> ProtocolResult<RespValue> {
        let line = Self::read_line(cursor)?;
        let num = line.parse::<i64>()
            .map_err(|_| ProtocolError::InvalidFormat("Invalid integer".to_string()))?;
        Ok(RespValue::Integer(num))
    }

    /// Parse bulk string: $6\r\nfoobar\r\n or $-1\r\n
    fn parse_bulk_string(cursor: &mut std::io::Cursor<&[u8]>) -> ProtocolResult<RespValue> {
        let line = Self::read_line(cursor)?;
        let length = line.parse::<i64>()
            .map_err(|_| ProtocolError::InvalidFormat("Invalid bulk string length".to_string()))?;

        if length == -1 {
            return Ok(RespValue::BulkString(None));
        }

        if length < 0 {
            return Err(ProtocolError::InvalidFormat("Invalid bulk string length".to_string()));
        }

        let length = length as usize;

        if cursor.remaining() < length + 2 { // +2 for \r\n
            return Err(ProtocolError::IncompleteData);
        }

        let mut data = vec![0u8; length];
        cursor.copy_to_slice(&mut data);

        // Read and validate \r\n
        if cursor.remaining() < 2 {
            return Err(ProtocolError::IncompleteData);
        }

        let cr = cursor.get_u8();
        let lf = cursor.get_u8();

        if cr != b'\r' || lf != b'\n' {
            return Err(ProtocolError::InvalidFormat("Missing \\r\\n".to_string()));
        }

        Ok(RespValue::BulkString(Some(Bytes::from(data))))
    }

    /// Parse array: *2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n or *-1\r\n
    fn parse_array(cursor: &mut std::io::Cursor<&[u8]>) -> ProtocolResult<RespValue> {
        let line = Self::read_line(cursor)?;
        let length = line.parse::<i64>()
            .map_err(|_| ProtocolError::InvalidFormat("Invalid array length".to_string()))?;

        if length == -1 {
            return Ok(RespValue::Array(None));
        }

        if length < 0 {
            return Err(ProtocolError::InvalidFormat("Invalid array length".to_string()));
        }

        let mut elements = Vec::with_capacity(length as usize);

        for _ in 0..length {
            let element = Self::parse_value(cursor)?;
            elements.push(element);
        }

        Ok(RespValue::Array(Some(elements)))
    }

    /// Read a line ending with \r\n
    fn read_line(cursor: &mut std::io::Cursor<&[u8]>) -> ProtocolResult<String> {
        let start_pos = cursor.position() as usize;
        let data = cursor.get_ref();

        // Find \r\n
        for i in start_pos..data.len() - 1 {
            if data[i] == b'\r' && data[i + 1] == b'\n' {
                let line_data = &data[start_pos..i];
                cursor.set_position((i + 2) as u64);

                return String::from_utf8(line_data.to_vec())
                    .map_err(|_| ProtocolError::InvalidFormat("Invalid UTF-8".to_string()));
            }
        }

        Err(ProtocolError::IncompleteData)
    }

    /// Parse a command from RESP array
    pub fn parse_command(value: RespValue) -> ProtocolResult<RedisCommand> {
        match value {
            RespValue::Array(Some(elements)) if !elements.is_empty() => {
                let mut args = Vec::new();

                for element in elements {
                    match element.as_string() {
                        Some(s) => args.push(s),
                        None => return Err(ProtocolError::InvalidArgumentType),
                    }
                }

                let command_name = args.remove(0).to_uppercase();
                Ok(RedisCommand::new(command_name, args))
            }
            _ => Err(ProtocolError::InvalidCommand("Command must be an array".to_string())),
        }
    }
}